package com.qf.ability.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.qf.ability.gateway.config.AuthIgnoreConfig;
import com.qf.ability.gateway.config.AuthUserTypeConfig;
import com.qf.ability.gateway.utils.ResponseUtils;
import com.qf.commons.core.utils.JwtUtils;
import com.qf.commons.data.base.BaseUser;
import com.qf.commons.data.result.R;
import com.qf.commons.data.result.RCodes;
import io.jsonwebtoken.lang.Collections;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.List;
import java.util.Map;

/**
 * 全局认证的过滤器
 *    1）全局过滤器
 *    2）局部过滤器
 *
 * author Ken
 * create_time 2022/9/1
 */
@Component
@Slf4j
//远程配置中心的刷新注解
@RefreshScope
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    /**
     * 注入需要忽略认证的请求的url集合
     */
    @Autowired(required = false)
    private AuthIgnoreConfig authIgnoreConfig;

    @Autowired
    private AuthUserTypeConfig authUserTypeConfig;

    /**
     * 匹配url的工具对象，可以进行通配符的匹配
     */
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //获取当前请求的url
        String url = exchange.getRequest().getURI().getPath();
        log.debug("[gateway auth filter] 网关全局认证过滤器！- {}", url);

        //无需登录 就可以访问
        if (authIgnoreConfig != null) {
            List<String> paths = authIgnoreConfig.getIgnore();
            if (!Collections.isEmpty(paths)) {
                //循环遍历所有需要忽略的url
                for (String ignorePath : paths) {
                    log.debug("[gateway auth filter] 需要忽略的url - {} 请求的url - {}", ignorePath, url);
                    if(antPathMatcher.match(ignorePath, url)){
                        //直接放行
                        log.debug("[gateway auth filter] 请求无需登录即可访问 - {}", url);
                        return chain.filter(exchange);
                    }
                }
            }
        }

        //需要登录才能访问
        //获取请求头
        HttpHeaders headers = exchange.getRequest().getHeaders();
        //获得请求来源
        String fromType = headers.getFirst("fromType");
        String jwtToken = headers.getFirst("loginToken");
        //校验JWT令牌
        String userJson = JwtUtils.parseJwtToken(jwtToken, "user");
        log.debug("[gateway auth filter] 获得用户的令牌解析的信息 - {}", userJson);
        if(userJson == null){
            //未登录，返回认证异常的信息
            R r = R.createFail(RCodes.AUTH_FAIL);
            return ResponseUtils.response(exchange, r);
        }

        //说明已经登录
        // 0 - com.qf.data.user.dto.SysUserPowersDto
        // 1 - com.qf.xxxxxxx
        Map<String, String> userTypeMap = authUserTypeConfig.getFromType();
        log.debug("[gateway auth filter] 获取请求来源 对应的 用户类型 - {}", userTypeMap);

        //根据请求来源 获取 对应的 实体类的全路径限定名
        //根据全路径限定名 获取 该类对应的Class对象
        Class uCls = null;
        try {
            uCls = Class.forName(userTypeMap.get(fromType));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }

        //将userJson -> SysUser
        BaseUser baseUser = (BaseUser) JSON.parseObject(userJson, uCls);
        log.debug("[gateway auth filter] 解析Token获得用户对象 - {}", baseUser);

        //----如果当前请求需要校验权限---
        //获取只需要登录就可以访问的loginPaths
        boolean isPowers = true;//是否需要权限校验
        List<String> loginPaths = authIgnoreConfig.getLogin();
        if (!Collections.isEmpty(loginPaths)) {
            for (String loginPath : loginPaths) {
                if(antPathMatcher.match(loginPath, url)){
                    //只需要登录就可以访问的url
                    isPowers = false;
                    break;
                }
            }
        }

        if (isPowers && !baseUser.hasPowers(url)) {
            //需要进行权限校验的 并且权限不足
            R r = R.createFail(RCodes.NO_POWER_ERROR);
            return ResponseUtils.response(exchange, r);
        }

        //将用户信息 继续 往后传递
        //封装一个新的请求头
        HttpHeaders newHttpHeader = new HttpHeaders();
        newHttpHeader.putAll(headers);//将旧的请求头设置给新的请求头对象
        newHttpHeader.add("uid", baseUser.getId() + "");//放入登录的用户id
        newHttpHeader.remove("loginToken");//移除登录的令牌，无需再往后传递

        //原本的请求uri对象
        URI oldUri = exchange.getRequest().getURI();
        //准备一个新的请求对象
        ServerHttpRequest newReuqest = exchange.getRequest().mutate().uri(oldUri).build();
        //将新的请求头封装到新的请求对象中
        newReuqest = new ServerHttpRequestDecorator(newReuqest) {
            @Override
            public HttpHeaders getHeaders() {
                return newHttpHeader;
            }
        };
        //放行
        return chain.filter(exchange.mutate().request(newReuqest).build());
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 100;
    }
}
